package encryptionAlgorithm

import (
	"bytes"
	"crypto/md5"
	"crypto/sha1"
	"fmt"
	"io"
	"math/rand"
	"os"
	"reflect"
	"strings"
)

type CheckSumTypeStruct struct{}

/*
CheckMd5 对字符串进行MD5哈希
*/
func (cs CheckSumTypeStruct) CheckMd5(data any) string {
	var (
		md5data string
		buffer  bytes.Buffer
	)
	switch fmt.Sprintf("%v", reflect.TypeOf(data)) {
	case "string":
		md5data = fmt.Sprintf("%v", data)
	case "[]*string":
		for _, v := range data.([]*string) {
			buffer.WriteString(fmt.Sprintf("%v", *v))
		}
		md5data = buffer.String()
		buffer.Reset()
	case "[][]*string":
		for _, v := range data.([][]*string) {
			for _, z := range v {
				buffer.WriteString(fmt.Sprintf("%v", *z))
			}
		}
		md5data = buffer.String()
		buffer.Reset()
	}
	t := md5.New()
	if _, err := io.WriteString(t, md5data); err != nil {
		return ""
	}
	sprintf := fmt.Sprintf("%x", t.Sum(nil))
	return sprintf
}

/*
CheckSha1 对字符串进行SHA1哈希
*/
func (cs CheckSumTypeStruct) CheckSha1(data string) string {
	t := sha1.New()
	if _, err := io.WriteString(t, data); err != nil {
		return ""
	}
	return fmt.Sprintf("%x", t.Sum(nil))
}

func (cs CheckSumTypeStruct) Arrcmap(src, dest []string) []string {
	msrc := make(map[string]byte) //按源数组建索引
	mall := make(map[string]byte) //源+目所有元素建索引
	var set []string              //交集
	//1、源数组建立map
	for _, v := range src {
		msrc[v] = 0
		mall[v] = 0
	}
	for _, v := range dest {
		l := len(mall)
		mall[v] = 1
		if l != len(mall) {
			l = len(mall)
		} else {
			set = append(set, v)
		}
	}
	return set
}

/*
singleLevelsPointArr 行数据集合指针比对
*/
func (cs CheckSumTypeStruct) singleLevelsPointArr(src, dest []*string) ([]*string, []*string) { //对比数据
	msrc := make(map[string]byte) //按目数组建索引
	mall := make(map[string]byte) //源+目所有元素建索引  并集
	var set []string              //交集
	//1.目数组建立map
	for _, v := range dest {
		if *v != "" {
			msrc[*v] = 0
			mall[*v] = 0
		}
	}
	//2.源数组中，存不进去，即重复元素，所有存不进去的集合就是并集
	for _, v := range src {
		if *v != "" {
			if val, ok := mall[*v]; ok && val == 0 {
				set = append(set, *v)
			}
			mall[*v] = 1
		}
	}
	//3.遍历交集，在并集中找，找到就从并集中删，删完后就是补集（即并-交=所有变化的元素）
	for _, v := range set {
		delete(mall, v)
	}
	//4.此时，mall是补集，所有元素去源中找，找到就是删除的，找不到的必定能在目数组中找到，即新加的
	var added, deleted []*string
	for v := range mall {
		_, exist := msrc[v]
		v1 := v
		if exist {
			deleted = append(deleted, &v1)
		} else {
			added = append(added, &v1)
		}
	}
	return added, deleted
}

// diffSlices 列数据集合指针比对
func (cs CheckSumTypeStruct) multipleLevelsPointArr(a, b [][]*string) (added, deleted [][]*string) {
	msrc := make(map[string]byte)    //按目数组建索引
	mallmd5 := make(map[string]byte) //源+目所有元素建索引  并集
	mall := make(map[string][]*string)
	var set []string //交集
	for _, v := range b {
		if len(v) > 0 {
			md5Str := cs.CheckMd5(v)
			msrc[md5Str] = 0
			mallmd5[md5Str] = 0
			mall[md5Str] = v
		}
	}
	//2.源数组中，存不进去，即重复元素，所有存不进去的集合就是并集
	for _, v := range a {
		if len(v) > 0 {
			md5Str := cs.CheckMd5(v)
			if val, ok := mallmd5[md5Str]; ok && val == 0 {
				set = append(set, md5Str)
			}
			mall[md5Str] = v
			mallmd5[md5Str] = 1
		}
	}
	//3.遍历交集，在并集中找，找到就从并集中删，删完后就是补集（即并-交=所有变化的元素）
	for _, v := range set {
		delete(mallmd5, v)
	}
	//4.此时，mall是补集，所有元素去源中找，找到就是删除的，找不到的必定能在目数组中找到，即新加的
	for v := range mallmd5 {
		_, exist := msrc[v]
		l := mall[v]
		if exist {
			deleted = append(deleted, l)
		} else {
			added = append(added, l)
		}
	}
	return
}
func (cs CheckSumTypeStruct) singleLevelsArr(src []string, dest []string) ([]string, []string) { //对比数据
	msrc := make(map[string]byte) //按目数组建索引
	mall := make(map[string]byte) //源+目所有元素建索引  并集
	var set []string              //交集
	//1.目数组建立map
	for _, v := range dest {
		if v != "" {
			msrc[v] = 0
			mall[v] = 0
		}
	}
	//2.源数组中，存不进去，即重复元素，所有存不进去的集合就是并集
	for _, v := range src {
		if v != "" {
			if val, ok := mall[v]; ok && val == 0 {
				set = append(set, v)
			}
			mall[v] = 1
		}
	}
	//3.遍历交集，在并集中找，找到就从并集中删，删完后就是补集（即并-交=所有变化的元素）
	for _, v := range set {
		delete(mall, v)
	}
	//4.此时，mall是补集，所有元素去源中找，找到就是删除的，找不到的必定能在目数组中找到，即新加的
	var added, deleted []string
	for v := range mall {
		_, exist := msrc[v]
		if exist {
			deleted = append(deleted, v)
		} else {
			added = append(added, v)
		}
	}
	return added, deleted
}
func (cs CheckSumTypeStruct) Arrcmp1(src any, dest any) (any, any) { //对比数据
	s := fmt.Sprintf("%v", reflect.TypeOf(src))
	d := fmt.Sprintf("%v", reflect.TypeOf(dest))
	switch {
	case s == d && s == "[][]*string":
		return cs.multipleLevelsPointArr(src.([][]*string), dest.([][]*string))
	case s == d && s == "[]*string":
		return cs.singleLevelsPointArr(src.([]*string), dest.([]*string))
	case s == d && s == "[]string":
		return cs.singleLevelsArr(src.([]string), dest.([]string))
	default:
		return nil, nil
	}
}

/*
Arrcmp 数据校验并输出差异性数据
*/
func (cs CheckSumTypeStruct) Arrcmp(src []string, dest []string) ([]string, []string) { //对比数据
	msrc := make(map[string]byte) //按目数组建索引
	mall := make(map[string]byte) //源+目所有元素建索引  并集
	var set []string              //交集
	//1.目数组建立map
	for _, v := range dest {
		if v != "" {
			msrc[v] = 0
			mall[v] = 0
		}
	}
	//2.源数组中，存不进去，即重复元素，所有存不进去的集合就是并集
	for _, v := range src {
		if v != "" {
			if val, ok := mall[v]; ok && val == 0 {
				set = append(set, v)
			}
			mall[v] = 1
		}
	}
	//3.遍历交集，在并集中找，找到就从并集中删，删完后就是补集（即并-交=所有变化的元素）
	for _, v := range set {
		delete(mall, v)
	}
	//4.此时，mall是补集，所有元素去源中找，找到就是删除的，找不到的必定能在目数组中找到，即新加的
	var added, deleted []string
	for v := range mall {
		_, exist := msrc[v]
		if exist {
			deleted = append(deleted, v)
		} else {
			added = append(added, v)
		}
	}
	return added, deleted
}
func (cs CheckSumTypeStruct) LowerArrCmp(src []string, dest []string) (add []string,eq []string, del []string) { //对比数据
	mSrc := make(map[string]byte) //按目数组建索引
	mAll := make(map[string]byte) //源+目所有元素建索引  并集
	var set []string              //交集
	//1.目数组建立map
	for _, v := range dest {
		if v != "" {
			newV := strings.ToLower(v)
			mSrc[newV] = 0
			mAll[newV] = 0
		}
	}
	//2.源数组中，存不进去，即重复元素，所有存不进去的集合就是并集
	for _, v := range src {
		if v != "" {
			newV := strings.ToLower(v)
			if val, ok := mAll[newV]; ok && val == 0 {
				set = append(set, newV)
				eq = append(eq, newV)
			}
			mAll[newV] = 1
		}
	}
	//3.遍历交集，在并集中找，找到就从并集中删，删完后就是补集（即并-交=所有变化的元素）
	for _, v := range set {
		delete(mAll, v)
	}
	//4.此时，mall是补集，所有元素去源中找，找到就是删除的，找不到的必定能在目数组中找到，即新加的
	for v := range mAll {
		_, exist := mSrc[v]
		if exist {
			del = append(del, v)
		} else {
			add = append(add, v)
		}
	}
	return add,eq,del
}
/*
Arrsame 根据两个切片找到相同的字符
*/
func (cs CheckSumTypeStruct) Arrsame(src, dest []string) string {
	msrc := make(map[string]byte) //按源数组建索引
	mall := make(map[string]byte) //源+目所有元素建索引
	var set string                //交集
	//1、源数组建立map
	for _, v := range src {
		msrc[v] = 0
		mall[v] = 0
	}
	for _, v := range dest {
		l := len(mall)
		mall[v] = 1
		if l != len(mall) {
			l = len(mall)
		} else {
			set = v
		}
	}
	return set
}

var defaultLetters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

// RandomString returns a random string with a fixed length
func (cs CheckSumTypeStruct) RandomString(n int, allowedChars ...[]rune) string {
	var letters []rune
	if len(allowedChars) == 0 {
		letters = defaultLetters
	} else {
		letters = allowedChars[0]
	}
	b := make([]rune, n)
	//rand.Seed(time.Now().UnixNano())
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}

/*
FileMd5 校验两个文件的md5值，是否一致
*/
func (cs CheckSumTypeStruct) FileMd5(f1 string) string {
	f, err := os.Open(f1)
	if err != nil {
		fmt.Println("Open", err)
		//return "", err
	}
	defer func() {
		if err2 := f.Close(); err2 != nil {
			fmt.Println(err2)
		}
	}()
	md5hash := md5.New()
	if _, err = io.Copy(md5hash, f); err != nil {
		fmt.Println("Copy", err)
		//return "", err
	}
	md5Val := fmt.Sprintf("%x", md5hash.Sum(nil))
	return md5Val
}

func CheckSum() *CheckSumTypeStruct {
	return &CheckSumTypeStruct{}
}
